import common from '../../../dxmodules/dxCommon.js'
import std from '../../../dxmodules/dxStd.js'
const utils = {}

// Check if it is null/undefined
utils.isEmpty = function (value) {
    return (value === null || value === undefined)
}
//Volume percentage conversion
utils.getVolume1 = function (volume1) {
    return 60 * (volume1 / 100)
}
//Convert hexadecimal little-endian to decimal
utils.hexLEToTimestamp = function (hexLE) {
    // Reverse the string and restore its original order.
    var hexString = '';
    for (var i = hexLE.length - 2; i >= 0; i -= 2) {
        hexString += hexLE.substr(i, 2);
    }

    // If the length of hexString is not even, add a leading zero to ensure that each byte has two characters.
    if (hexString.length % 2 !== 0) {
        hexString = '0' + hexString;
    }

    // Convert a hexadecimal string to a timestamp.
    var timestamp = parseInt(hexString, 16);

    return timestamp;
}

//Decimal to Hexadecimal Little Endian Conversion
utils.timestampToHexLE = function (timestamp, length) {
    // Convert the timestamp to a hexadecimal string.
    var hexString = timestamp.toString(16);

    // If the length of hexString is not even, add a leading zero to ensure that each byte has two characters.
    if (hexString.length % 2 !== 0) {
        hexString = '0' + hexString;
    }

    // Reverse the string to implement little-endian mode.
    var reversedHexString = '';
    for (var i = hexString.length - 2; i >= 0; i -= 2) {
        reversedHexString += hexString.substr(i, 2);
    }

    // If the length of the hexadecimal string is less than 8 characters, add zeros at the end.
    while (reversedHexString.length < length) {
        reversedHexString += '0';
    }

    return reversedHexString;
}

//JSON to Microlight format {key=1}
utils.jsonObjectToString = function (obj) {
    if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
        throw new Error('Input must be a valid JSON object.');
    }

    const entries = Object.entries(obj);
    const keyValuePairs = entries.map(([key, value]) => {
        let formattedValue;
        if (typeof value === 'string') {
            formattedValue = `"${value}"`; // String types need to be enclosed in double quotes.
        } else if (typeof value === 'number') {
            formattedValue = value.toString(); // Numbers directly converted to strings
        } else {
            throw new Error('Unsupported value type. Only strings and numbers are supported.');
        }
        return `${key}=${formattedValue}`;
    });

    return `{${keyValuePairs.join(',')}}`;
}

utils.decimalToLittleEndian = function (dec) {
    // Convert a decimal number to a hexadecimal string.
    let hexString = dec.toString(16);

    // If the length of the hexadecimal string is odd, pad it with leading zeros.
    if (hexString.length % 2 !== 0) {
        hexString = '0' + hexString;
    }

    // Split the hexadecimal string into groups of two characters each, then sort them in reverse order according to little-endian format.
    let littleEndianHexString = hexString.match(/.{1,2}/g).reverse().join('');

    return littleEndianHexString;
}

/**
 * Convert the parsed string to JSON, and note that the value should not contain a single quote (").
 * @param {*} inputString 
 * @returns 
 */
utils.parseString = function (inputString) {
    // Get the contents between {}
    inputString = inputString.slice(inputString.indexOf("{"), inputString.lastIndexOf("}") + 1)
    // The regular expression `key=value` specifies the key, which is `\w+` (alphanumeric underscores, case sensitive), and spaces are allowed on both sides of the `=` symbol. The value is either `\w+` or the content between two consecutive `"` characters (including `"`).
    const keyValueRegex = /(\w+)\s*=\s*("[^"]*"|\w+)/g;
    let jsonObject = {};
    let match;
    while ((match = keyValueRegex.exec(inputString)) !== null) {
        let key = match[1];
        let value = match[2]

        if (/^\d+$/.test(value)) {
            // number
            value = parseInt(value)
        } else if (/^\d+\.\d+$/.test(value)) {
            // Decimals
            value = parseFloat(value)
        } else if (value == 'true') {
            value = true
        } else if (value == 'false') {
            value = false
        } else {
            // String
            value = value.replace(/"/g, '')
        }
        jsonObject[key] = value;
    }
    return jsonObject;
}
utils.formatUnixTimestamp = function (timestamp) {
    const date = new Date(timestamp);
    const year = date.getFullYear().toString();
    const month = padZero(date.getMonth() + 1);
    const day = padZero(date.getDate());
    const hours = padZero(date.getHours());
    const minutes = padZero(date.getMinutes());
    const seconds = padZero(date.getSeconds());
    return `${month}${day}${hours}${minutes}${year}`;
}

function padZero(number) {
    return number < 10 ? '0' + number : number.toString();
}
// Get the download size (in bytes) of the URL file.
utils.getUrlFileSize = function (url) {
    let actualSize = common.systemWithRes(`wget --spider -S ${url} 2>&1 | grep 'Length' | awk '{print $2}'`, 100).match(/\d+/g)
    return actualSize ? parseInt(actualSize) : 0
}

/**
 * Wait for the download result. Note that the timeout period must not exceed the dog-feeding time, otherwise the download will restart if it is slow.
 * @param {*} update_addr Download link
 * @param {*} downloadPath Storage path
 * @param {*} timeout time out
 * @param {*} update_md5 MD5 checksum
 * @param {*} fileSize File size
 * @returns Download results
 */
utils.waitDownload = function (update_addr, downloadPath, timeout, update_md5, fileSize) {
    // Delete the original file
    common.systemBrief(`rm -rf "${downloadPath}"`)
    if (fileSize == 0) {
        return false
    }
    // Asynchronous download
    common.systemBrief(`wget -c "${update_addr}" -O "${downloadPath}" &`)
    let startTime = new Date().getTime()
    while (true) {
        // Calculate the size of the downloaded files
        let size = parseInt(common.systemWithRes(`file="${downloadPath}"; [ -e "$file" ] && wc -c "$file" | awk '{print $1}' || echo "0"`, 100).split(/\s/g)[0])
        // If they are equal, the download was successful.
        if (size == fileSize) {
            let ret = common.md5HashFile(downloadPath)
            if (ret) {
                let md5 = ret.map(v => v.toString(16).padStart(2, '0')).join('')
                if (md5 == update_md5) {
                    // MD5 checksum success returns true.
                    return true
                }
            }
            common.systemBrief(`rm -rf "${downloadPath}"`)
            // MD5 checksum failure returns false.
            return false
        }
        // If the download times out, delete the downloaded file and restart the computer to stop asynchronous downloads.
        if (new Date().getTime() - startTime > timeout) {
            driver.pwm.fail()
            common.systemBrief(`rm -rf "${downloadPath}"`)
            common.asyncReboot(3)
            return false
        }
        std.sleep(100)
    }
}

const daysOfWeekEn = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const monthsOfYearEn = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
// Get formatting time
utils.getDateTime = function () {
    let t = new Date();
    let addZero = (v) => {
        // Double-digit padding with 0
        return v.toString(10).padStart(2, '0')
    }
    return {
        year: t.getFullYear(),//Years, such as 2024
        month: addZero(t.getMonth() + 1), // Months start from 0, so you need to add 1.
        monthTextEn: monthsOfYearEn[t.getMonth()],
        day: addZero(t.getDate()), // Get Date
        hours: addZero(t.getHours()),// Get Hours
        minutes: addZero(t.getMinutes()),// Get minutes
        seconds: addZero(t.getSeconds()),// Get seconds
        dayTextEn: daysOfWeekEn[t.getDay()],//Weekday
    }
}

utils.convertTo12HourFormat = function (hours, minutes) {
    let ampm = hours >= 12 ? 'PM' : 'AM'
    hours = hours % 12
    hours = hours ? hours : 12 // Convert 0 to 12.
    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')} ${ampm}`
}

export default utils
